<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <title>BIO、NIO、AIO  &middot; sitename</title>
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">


<meta name="description" content="BIO和NIO的理解 description" />

<meta name="keywords" content="one, two, ">


<meta property="og:title" content="BIO、NIO、AIO  &middot; sitename ">
<meta property="og:site_name" content="sitename"/>
<meta property="og:url" content="https://lvzongcheng.gitee.io/java/bionioaio/" />
<meta property="og:locale" content="en-EN">


<meta property="og:type" content="article" />
<meta property="og:description" content="BIO和NIO的理解 description"/>
<meta property="og:article:published_time" content="2017-05-13T00:00:00Z" />
<meta property="og:article:modified_time" content="2017-05-13T00:00:00Z" />

  
    
<meta property="og:article:tag" content="one">
    
<meta property="og:article:tag" content="two">
    
  

  

<script type="application/ld+json">
  {
    "@context": "http://schema.org",
    "@type": "Article",
    "headline": "BIO、NIO、AIO",
    "author": {
      "@type": "Person",
      "name": "LvZongCheng"
    },
    "datePublished": "2017-05-13",
    "description": "BIO和NIO的理解 description",
    "wordCount":  488 
  }
</script>



<link rel="canonical" href="https://lvzongcheng.gitee.io/java/bionioaio/" />

<link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://lvzongcheng.gitee.io/touch-icon-144-precomposed.png">
<link href="https://lvzongcheng.gitee.io/favicon.png" rel="icon">

<meta name="generator" content="Hugo 0.60.0" />

  <!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->

<link href='https://fonts.googleapis.com/css?family=Merriweather:300%7CRaleway%7COpen+Sans' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/css/font-awesome.min.css">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/highlight/default.css">

  
  
	<script>
	  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
	  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
	  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
	  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

	  ga('create', 'Your Google Analytics tracking code', 'auto');
	  ga('send', 'pageview');

	</script>

</head>
<body>
  <main id="main-wrapper" class="container main_wrapper has-sidebar">
    <header id="main-header" class="container main_header">
  <div class="container brand">
  <div class="container title h1-like">
  <a class="baselink" href="https://lvzongcheng.gitee.io">
  LvZongCheng

</a>

</div>

  
<div class="container topline">
  
  淡定 执著 进取 自律


</div>


</div>

  <nav class="container nav primary no-print">
  

<a class="homelink" href="https://lvzongcheng.gitee.io">home</a>


  
<a href="https://lvzongcheng.gitee.io/framework" title="Show list of Frameworks">Frameworks</a>

<a href="https://lvzongcheng.gitee.io/java" title="Show list of Java">Java</a>

<a href="https://lvzongcheng.gitee.io/linux" title="Show list of Linux">Linux</a>

<a href="https://lvzongcheng.gitee.io/redis" title="Show list of Redis">Redis</a>

<a href="https://lvzongcheng.gitee.io/sql" title="Show list of SQL">SQL</a>

<a href="https://lvzongcheng.gitee.io/Spring" title="Show list of Spring">Spring</a>

<a href="https://lvzongcheng.gitee.io/tools" title="Show list of Tools">Tools</a>

<a href="https://lvzongcheng.gitee.io/webpage" title="Show list of WebPage">WebPage</a>

<a href="https://lvzongcheng.gitee.io/information" title="Show list of information">information</a>

<a href="https://lvzongcheng.gitee.io/network" title="Show list of network">network</a>

<a href="https://lvzongcheng.gitee.io/%E5%BE%AE%E6%9C%8D%E5%8A%A1" title="Show list of 微服务">微服务</a>

<a href="https://lvzongcheng.gitee.io/%E6%80%BB%E7%BB%93" title="Show list of 总结">总结</a>

<a href="https://lvzongcheng.gitee.io/%E6%9C%BA%E5%99%A8%E8%87%AA%E5%8A%A8%E5%8C%96" title="Show list of 机器自动化">机器自动化</a>

<a href="https://lvzongcheng.gitee.io/%E6%B1%87%E7%BC%96" title="Show list of 汇编">汇编</a>

<a href="https://lvzongcheng.gitee.io/%E8%AE%B0%E4%BA%8B%E6%9C%AC" title="Show list of 记事本">记事本</a>


</nav>

<div class="container nav secondary no-print">
  
<a id="contact-link-email" class="contact_link" rel="me" aria-label="Email" href="mailto:Lyc19880405@163.com">
  <span class="fa fa-envelope-square"></span></a>



<a id="contact-link-github" class="contact_link" rel="me" aria-label="Github" href="https://github.com/enten/hugo-boilerplate">
  <span class="fa fa-github-square"></span></a>




 


















</div>


  

</header>


<article id="main-content" class="container main_content single">
  <header class="container hat">
  <h1>BIO、NIO、AIO
</h1>

</header>

  <div class="container content">
  <h5 id="heading">简介：</h5>
<p>IO的方式通常分为几种，同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。</p>
<p>BIO：同步阻塞式IO，服务器实现模式为一个连接一个线程，即客户端有连接请求时服务器端就
需要启动一个线程进行处理，如果这个连接不做任何事情会造成不必要的线程开销，当然可以通
过线程池机制改善。 
BIO在JDK1.4出来之前，我们建立网络连接的时候采用BIO模式，需要先在服务端启动一个ServerSocket，
然后在客户端启动Socket来对服务端进行通信，默认情况下服务端需要对每个请求建立一堆线程
等待请求，而客户端发送请求后，先咨询服务端是否有线程相应，如果没有则会一直等待或者遭到
拒绝请求，如果有的话，客户端会线程会等待请求结束后才继续执行</p>
<p>NIO：同步非阻塞式IO，服务器实现模式为一个请求一个线程，即客户端发送的连接请求都会注
册到多路复用器上，多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 
AIO(NIO.2)：异步非阻塞式IO，服务器实现模式为一个有效请求一个线程，客户端的I/O请求都
是由OS先完成了再通知服务器应用去启动线程进行处理。 
NIO本身是基于事件驱动思想来完成的，其主要想解决的是BIO的大并发问题： 在使用同步I/O的
网络应用中，如果要同时处理多个客户端请求，或是在客户端要同时和多个服务器进行通讯，就
必须使用多线程来处理。也就是说，将每一个客户端请求分配给一个线程来单独处理。这样做虽
然可以达到我们的要求，但同时又会带来另外一个问题。由于每创建一个线程，就要为这个线程
分配一定的内存空间（也叫工作存储器），而且操作系统本身也对线程的总数有一定的限制。如
果客户端的请求过多，服务端程序可能会因为不堪重负而拒绝客户端的请求，甚至服务器可能会
因此而瘫痪。
NIO基于Reactor，当socket有流可读或可写入socket时，操作系统会相应的通知引用程序进行
处理，应用再将流读取到缓冲区或写入操作系统。  也就是说，这个时候，已经不是一个连接就要
对应一个处理线程了，而是有效的请求，对应一个线程，当连接没有数据时</p>
<p>AIO：
AIO与NIO不同，当进行读写操作时，只须直接调用API的read或write方法即可。这两种方法均为异步
的，对于读操作而言，当有流可读取时，操作系统会将可读的流传入read方法的缓冲区，并通知应
用程序；对于写操作而言，当操作系统将write方法传递的流写入完毕时，操作系统主动通知应用程
序。  即可以理解为，read/write方法都是异步的，完成后会主动调用回调函数。  在JDK1.7中，这
部分内容被称作NIO.2，主要在Java.nio.channels包下增加了下面四个异步通道：</p>
<pre><code>    AsynchronousSocketChannel
    AsynchronousServerSocketChannel
    AsynchronousFileChannel
    AsynchronousDatagramChannel
</code></pre><p>其中的read/write方法，会返回一个带</p>
<p>BIO与NIO一个比较重要的不同，是我们使用BIO的时候往往会引入多线程，每个连接一个单独的
线程；而NIO则是使用单线程或者只使用少量的多线程，每个连接共用一个线程。</p>
<h5 id="bio">BIO</h5>
<p>同步阻塞式IO，相信每一个学习过操作系统网络编程或者任何语言的网络编程的人都很熟悉，
在while循环中服务端会调用accept方法等待接收客户端的连接请求，一旦接收到一个连接请求，
就可以建立通信套接字在这个通信套接字上进行读写操作，此时不能再接收其他客户端连接请求，
只能等待同当前连接的客户端的操作执行完成。 
如果BIO要能够同时处理多个客户端请求，就必须使用多线程，即每次accept阻塞等待来自客户
端请求，一旦受到连接请求就建立通信套接字同时开启一个新的线程来处理这个套接字的数据读
写请求，然后立刻又继续accept等待其他客户端连接请求，即为每一个客户端连接请求都创建一
个线程来单独处理，大概原理图就像这样： 
<img src="/image/Java/20170726-01.png" alt="Alt text"></p>
<p>虽然此时服务器具备了高并发能力，即能够同时处理多个客户端请求了，但是却带来了一个问题，
随着开启的线程数目增多，将会消耗过多的内存资源，导致服务器变慢甚至崩溃，NIO可以一定
程度解决这个问题。</p>
<h5 id="nio">NIO</h5>
<p>同步非阻塞式IO，关键是采用了事件驱动的思想来实现了一个多路转换器。 
NIO与BIO最大的区别就是只需要开启一个线程就可以处理来自多个客户端的IO事件，这是怎么做到的呢？ 
就是多路复用器，可以监听来自多个客户端的IO事件： 
A. 若服务端监听到客户端连接请求，便为其建立通信套接字(java中就是通道)，然后返回继续监听，
若同时有多个客户端连接请求到来也可以全部收到，依次为它们都建立通信套接字。 
B. 若服务端监听到来自已经创建了通信套接字的客户端发送来的数据，就会调用对应接口处理接收
到的数据，若同时有多个客户端发来数据也可以依次进行处理。 
C. 监听多个客户端的连接请求和接收数据请求同时还能监听自己时候有数据要发送。 
<img src="/image/Java/20170726-02.png" alt="Alt text"></p>
<p>总之就是在一个线程中就可以调用多路复用接口（java中是select）阻塞同时监听来自多个客户端的IO请
求，一旦有收到IO请求就调用对应函数处理。</p>
<p>各自应用场景</p>
<p>到这里你也许已经发现，一旦有请求到来(不管是几个同时到还是只有一个到)，都会调用对应IO处理函数处理，所以：</p>
<p>（1）NIO适合处理连接数目特别多，但是连接比较短（轻操作）的场景，Jetty，Mina，ZooKeeper等都是基于java nio实现。</p>
<p>（2）BIO方式适用于连接数目比较小且固定的场景，这种方式对服务器资源要求比较高，并发局限于应用中。</p>
<p>附录：下面附上一个别人写的java NIO的例子。 
服务端：</p>
<pre><code>     package cn.nio;    
        
     import java.io.IOException;    
     import java.net.InetSocketAddress;    
     import java.nio.ByteBuffer;    
     import java.nio.channels.SelectionKey;    
     import java.nio.channels.Selector;    
     import java.nio.channels.ServerSocketChannel;    
     import java.nio.channels.SocketChannel;    
     import java.util.Iterator;    
        
     /**  
     * NIO服务端  
     *  
     */    
     public class NIOServer {    
        //通道管理器    
        private Selector selector;    
        
        /**  
         * 获得一个ServerSocket通道，并对该通道做一些初始化的工作  
         * @param port  绑定的端口号  
         * @throws IOException  
         */    
        public void initServer(int port) throws IOException {    
            // 获得一个ServerSocket通道    
            ServerSocketChannel serverChannel = ServerSocketChannel.open();    
            // 设置通道为非阻塞    
            serverChannel.configureBlocking(false);    
            // 将该通道对应的ServerSocket绑定到port端口    
            serverChannel.socket().bind(new InetSocketAddress(port));    
            // 获得一个通道管理器    
            this.selector = Selector.open();    
            //将通道管理器和该通道绑定，并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后，    
            //当该事件到达时，selector.select()会返回，如果该事件没到达selector.select()会一直阻塞。    
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);    
        }    
        
        /**  
         * 采用轮询的方式监听selector上是否有需要处理的事件，如果有，则进行处理  
         * @throws IOException  
         */    
        @SuppressWarnings(&quot;unchecked&quot;)    
        public void listen() throws IOException {    
            System.out.println(&quot;服务端启动成功！&quot;);    
            // 轮询访问selector    
            while (true) {    
                //当注册的事件到达时，方法返回；否则,该方法会一直阻塞    
                selector.select();    
                // 获得selector中选中的项的迭代器，选中的项为注册的事件    
                Iterator ite = this.selector.selectedKeys().iterator();    
                while (ite.hasNext()) {    
                    SelectionKey key = (SelectionKey) ite.next();    
                    // 删除已选的key,以防重复处理    
                    ite.remove();    
                    // 客户端请求连接事件    
                    if (key.isAcceptable()) {    
                        ServerSocketChannel server = (ServerSocketChannel) key    
                                .channel();    
                        // 获得和客户端连接的通道    
                        SocketChannel channel = server.accept();    
                        // 设置成非阻塞    
                        channel.configureBlocking(false);    
        
                        //在这里可以给客户端发送信息哦    
                        channel.write(ByteBuffer.wrap(new String(&quot;向客户端发送了一条信息&quot;).getBytes()));    
                        //在和客户端连接成功之后，为了可以接收到客户端的信息，需要给通道设置读的权限。    
                        channel.register(this.selector, SelectionKey.OP_READ);    
                            
                        // 获得了可读的事件    
                    } else if (key.isReadable()) {    
                            read(key);    
                    }    
        
                }    
        
            }    
        }    
        /**  
         * 处理读取客户端发来的信息 的事件  
         * @param key  
         * @throws IOException   
         */    
        public void read(SelectionKey key) throws IOException{    
            // 服务器可读取消息:得到事件发生的Socket通道    
            SocketChannel channel = (SocketChannel) key.channel();    
            // 创建读取的缓冲区    
            ByteBuffer buffer = ByteBuffer.allocate(10);    
            channel.read(buffer);    
            byte[] data = buffer.array();    
            String msg = new String(data).trim();    
            System.out.println(&quot;服务端收到信息：&quot;+msg);    
            ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());    
            channel.write(outBuffer);// 将消息回送给客户端    
        }    
            
        /**  
         * 启动服务端测试  
         * @throws IOException   
        */    
       public static void main(String[] args) throws IOException {    
           NIOServer server = new NIOServer();    
           server.initServer(8000);    
           server.listen();    
       }    
       
    }    
</code></pre><p>客户端：</p>
<pre><code>     package cn.nio;    
       
    import java.io.IOException;    
    import java.net.InetSocketAddress;    
    import java.nio.ByteBuffer;    
    import java.nio.channels.SelectionKey;    
    import java.nio.channels.Selector;    
    import java.nio.channels.SocketChannel;    
    import java.util.Iterator;    
        
     /**  
     * NIO客户端  
     *  
     */    
     public class NIOClient {    
        //通道管理器    
        private Selector selector;    
        
        /**  
         * 获得一个Socket通道，并对该通道做一些初始化的工作  
         * @param ip 连接的服务器的ip  
         * @param port  连接的服务器的端口号           
         * @throws IOException  
         */    
        public void initClient(String ip,int port) throws IOException {    
            // 获得一个Socket通道    
            SocketChannel channel = SocketChannel.open();    
            // 设置通道为非阻塞    
            channel.configureBlocking(false);    
            // 获得一个通道管理器    
            this.selector = Selector.open();    
                
            // 客户端连接服务器,其实方法执行并没有实现连接，需要在listen（）方法中调    
            //用channel.finishConnect();才能完成连接    
            channel.connect(new InetSocketAddress(ip,port));    
            //将通道管理器和该通道绑定，并为该通道注册SelectionKey.OP_CONNECT事件。    
            channel.register(selector, SelectionKey.OP_CONNECT);    
        }    
        
        /**  
         * 采用轮询的方式监听selector上是否有需要处理的事件，如果有，则进行处理  
         * @throws IOException  
         */    
        @SuppressWarnings(&quot;unchecked&quot;)    
        public void listen() throws IOException {    
            // 轮询访问selector    
            while (true) {    
                selector.select();    
                // 获得selector中选中的项的迭代器    
                Iterator ite = this.selector.selectedKeys().iterator();    
                while (ite.hasNext()) {    
                    SelectionKey key = (SelectionKey) ite.next();    
                    // 删除已选的key,以防重复处理    
                    ite.remove();    
                    // 连接事件发生    
                    if (key.isConnectable()) {    
                        SocketChannel channel = (SocketChannel) key    
                                .channel();    
                        // 如果正在连接，则完成连接    
                        if(channel.isConnectionPending()){    
                            channel.finishConnect();    
                                
                        }    
                        // 设置成非阻塞    
                        channel.configureBlocking(false);    
        
                        //在这里可以给服务端发送信息哦    
                        channel.write(ByteBuffer.wrap(new String(&quot;向服务端发送了一条信息&quot;).getBytes()));    
                        //在和服务端连接成功之后，为了可以接收到服务端的信息，需要给通道设置读的权限。    
                        channel.register(this.selector, SelectionKey.OP_READ);    
                            
                        // 获得了可读的事件    
                    } else if (key.isReadable()) {    
                            read(key);    
                    }    
        
                }    
        
            }    
        }    
        /**  
         * 处理读取服务端发来的信息 的事件  
         * @param key  
         * @throws IOException   
         */    
        public void read(SelectionKey key) throws IOException{    
            //和服务端的read方法一样    
        }    
            
            
        /**  
         * 启动客户端测试  
         * @throws IOException   
         */    
        public static void main(String[] args) throws IOException {    
            NIOClient client = new NIOClient();    
            client.initClient(&quot;localhost&quot;,8000);    
            client.listen();    
        }    
        
     }  
</code></pre>
</div>


  
</article>
      <footer id="main-footer" class="container main_footer">
  

  <div class="container nav foot no-print">
  
<a href="https://lvzongcheng.gitee.io/license">license</a>


  <a class="toplink" href="#">back to top</a>

</div>

  <div class="container credits">
  
<div class="container footline">
  
  code with <!-- raw HTML omitted --><!-- raw HTML omitted -->


</div>


  
<div class="container copyright">
  
  (c) 2015 yourname.


</div>


</div>

</footer>

    </main>
    
<script type="text/javascript">
  (function() {
    
    
    if (window.location.hostname == "localhost")
      return;
    var dsq = document.createElement('script'); dsq.async = true; dsq.type = 'text/javascript';
    dsq.src = '//your_disqus_shortname.disqus.com/count.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
  })();
</script>



<script src="/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>



    
  </body>
</html>

